﻿#region The MIT License
// The MIT License
// 
// Copyright (c) 2009 octalforty studios
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#endregion
using Irony.Parsing;

namespace octalforty.Chinchillin.Console.Compiler
{
    public class CdmGrammar : Grammar
    {
        public CdmGrammar()
        {
            // 
            // Terminals
            var number = new NumberLiteral("number", NumberOptions.IntOnly);
            var identifier = new IdentifierTerminal("identifier", "-", "");
            
            var @string = new StringLiteral("string", "\"", StringOptions.AllowsAllEscapes);
            @string.AddPrefix("@", StringOptions.NoEscapes | StringOptions.AllowsLineBreak | StringOptions.AllowsDoubledQuote);

            var singleLineComment = new CommentTerminal("singleLineComent", "#", "\n", "\r");
            var multiLineComment = new CommentTerminal("multiLineComment", "/*", "*/");

            NonGrammarTerminals.Add(singleLineComment);
            NonGrammarTerminals.Add(multiLineComment);

            var comma = ToTerm(",");
            var colon = ToTerm(":");
            var assignment = ToTerm("=>");
            var leftSquareBracket = ToTerm("[");
            var rightSquareBracket = ToTerm("]");

            //
            // Non-terminals
            var term = new NonTerminal("term");
            var termList = new NonTerminal("termList");
            var list = new NonTerminal("list");

            var propertyAssignment = new NonTerminal("propertyAssignment");
            var propertyAssignmentList = new NonTerminal("propertyAssignmentList");

            var deploymentManifest = new NonTerminal("deploymentManifest");
            var defs = new NonTerminal("deploymentManifestBlock");

            var commit = new NonTerminal("commit");

            var def = new NonTerminal("definition");

            var defServer = new NonTerminal("serverDefinition");
            var defTask = new NonTerminal("taskDefinition");
            
            var exec = new NonTerminal("exec");
            var execs = new NonTerminal("execs");
            
            var rollback = new NonTerminal("rollback");

            var end = ToTerm("end") + Eos;

            //
            // BNF rules
            term.Rule = number | identifier | @string | list;
            termList.Rule = MakeStarRule(termList, comma, term);
            list.Rule = leftSquareBracket + termList + rightSquareBracket;

            // identifier => term
            propertyAssignment.Rule = identifier + assignment + term;

            // identifier => term (, identifier => term)*
            propertyAssignmentList.Rule = MakeStarRule(propertyAssignmentList, comma, propertyAssignment);

            // deployment-manifest x => y
            //  defList
            // end
            deploymentManifest.Rule = ToTerm("deployment-manifest") + propertyAssignmentList + Eos + defs + end;

            defs.Rule = MakeStarRule(defs, def);

            def.Rule = ToTerm("def") + (defServer | defTask); //("task" + propertyAssignmentList + Eos + execs + end | "server" + propertyAssignmentList + Eos);

            defServer.Rule = ToTerm("server") + propertyAssignmentList + Eos;
            defTask.Rule = ToTerm("task") + propertyAssignmentList + Eos + commit + end;

            commit.Rule = execs + rollback | execs;

            exec.Rule = ToTerm("exec") + identifier + colon + identifier + propertyAssignmentList + Eos;
            execs.Rule = MakeStarRule(execs, exec);

            rollback.Rule = ToTerm("rollback") + Eos + execs + end;

            

            Root = deploymentManifest;
            LanguageFlags = LanguageFlags.DisableScannerParserLink | LanguageFlags.CreateAst;
        }

        public override void CreateTokenFilters(LanguageData language, TokenFilterList filters) 
        {
            var outlineFilter = new CodeOutlineFilter(language.GrammarData, /*OutlineOptions.ProduceIndents |*/ OutlineOptions.CheckBraces, ToTerm(@"\"));
            filters.Add(outlineFilter);
        }
    }
}
